/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package operator.server;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import share.MessageType;
import share.Server;
import share.SocketCommunicator;

/**
 *
 * @author mire
 */
public class OperatorOperatorThread implements Runnable {

    private Socket clientSocket;
    private SocketCommunicator socComm;
    private OperatorServer operatorServer;
    private final SimpleDateFormat dateFormat;

    public OperatorOperatorThread(Socket clientSocket, OperatorServer operatorServer) {
        this.clientSocket = clientSocket;
        this.operatorServer = operatorServer;
        this.dateFormat = new SimpleDateFormat("HH:mm:ss dd.MM.yyyy");
    }

    @Override
    public void run() {
        String status = null;
        boolean error = false;
        String threadInfo = dateFormat.format(new Date());
        threadInfo += " " + this.hashCode() + " ";
        try {
            socComm = new SocketCommunicator(clientSocket, operatorServer.getWaitForAnswerTime());
            socComm.open();
            String request = socComm.readString();

            threadInfo += "operator " + socComm.getAddress() + " " + request + " ";
            System.out.println(threadInfo);

            MessageType msg;
            try {
                msg = MessageType.valueOf(request);
            } catch (IllegalArgumentException ex) {
                status = "ERROR_NO_REQUEST";
                System.out.println(threadInfo + status);
                socComm.writeString(status);
                socComm.close();
                return;
            }

            switch (msg) {
                case ALIVE: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    socComm.writeString("YES");
                    String address = socComm.getAddress();
                    socComm.close();

                    ConcurrentHashMap<Integer, Server> servers = operatorServer.getServers();
                    for (Iterator<Server> it = servers.values().iterator(); it.hasNext();) {
                        Server server = it.next();
                        if (server.getAddress().equals(address)) {
                            server.setOnline(true);
                        }
                    }
                    break;
                }
                //</editor-fold>

                case NEW_CLIENT: //<editor-fold defaultstate="collapsed" desc="...">
                {

                    if (!operatorServer.getServers().contains(new Server(socComm.getAddress()))) {
                        status = "ERROR_UNKNOWN_SERVER";
                        System.out.println(threadInfo + status);
                        socComm.writeString(status);
                        socComm.close();
                        break;
                    }

                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    String clientAddress = socComm.readString();

                    status = "ADDING_CLIENT";
                    System.out.println(threadInfo + status);

                    operatorServer.getClients().add(clientAddress);
                    operatorServer.backupClients();

                    status = "CLIENT_ADDED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);
                    socComm.close();
                    break;
                }
                //</editor-fold>

                case NEW_SERVER: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    if (operatorServer.getServers().isEmpty()) {
                        //staviti sebe
                        Server self = new Server(operatorServer.getThreadNo(), operatorServer.getCentralPort());
                        operatorServer.getServers().put(self.hashCode(), self);
                    }

                    Server newServer = null;
                    newServer = (Server) socComm.readObject();
                    operatorServer.getServers().put(newServer.hashCode(), newServer);
                    operatorServer.backupServers();

                    status = "SERVER_ADDED";
                    System.out.println(threadInfo + status);

                    socComm.writeObject(operatorServer.getLastTimeSynchronized());
                    status = socComm.readString();
                    System.out.println(threadInfo + status);
                    if (status.equals("SYNCHRONIZED_ALREADY")) {
                        break;
                    }
                    status = "SENDING_DATA";
                    System.out.println(threadInfo + status);
                    socComm.writeObject(operatorServer.getUsers());
                    socComm.writeObject(operatorServer.getCentralDownUsers());
                    socComm.writeObject(operatorServer.getNumbers());
                    socComm.writeObject(operatorServer.getServers());
                    socComm.writeObject(operatorServer.getClients());
                    String fileLog = operatorServer.getTransferLog().getAllLog();
                    socComm.writeString(fileLog);

                    status = socComm.readString();
                    System.out.println(threadInfo + status);
                    socComm.close();
                    break;
                }
                //</editor-fold>

                case NEW_USER: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    OperatorUser newUser;
                    Date synchronizedTime;

                    if (!operatorServer.getServers().contains(new Server(socComm.getAddress()))) {
                        status = "ERROR_UNKNOWN_SERVER";
                        System.out.println(threadInfo + status);
                        socComm.writeString(status);
                        socComm.close();
                        break;
                    }

                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    newUser = (OperatorUser) socComm.readObject();
                    synchronizedTime = (Date) socComm.readObject();

                    status = "ADDING_USER";
                    System.out.println(threadInfo + status);

                    if (operatorServer.isCentralServerOnline()) {
                        operatorServer.getUsers().putIfAbsent(newUser.getId(), newUser);
                        operatorServer.backupUsers();
                    } else {
                        operatorServer.getCentralDownUsers().put(newUser.getId(), newUser);
                    }
                    operatorServer.setLastTimeSynchronized(synchronizedTime);
                    operatorServer.backupConfiguration();

                    status = "USER_ADDED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);
                    socComm.close();
                    break;
                }
                //</editor-fold>

                case NEW_MULTIPLE_USERS: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    ConcurrentHashMap<Integer, OperatorUser> newUsers;
                    ArrayList<String> newNumbers;
                    Date synchronizedTime;

                    if (!operatorServer.getServers().contains(new Server(socComm.getAddress()))) {
                        status = "ERROR_UNKNOWN_SERVER";
                        System.out.println(threadInfo + status);
                        socComm.writeString(status);
                        socComm.close();
                        break;
                    }

                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    newNumbers = (ArrayList<String>) socComm.readObject();
                    newUsers = (ConcurrentHashMap<Integer, OperatorUser>) socComm.readObject();
                    synchronizedTime = (Date) socComm.readObject();

                    status = "ADDING_MULTIPLE_USERS";
                    System.out.println(threadInfo + status);

                    operatorServer.getNumbers().addAll(newNumbers);
                    if (operatorServer.isCentralServerOnline()) {
                        operatorServer.getUsers().putAll(newUsers);
                        operatorServer.backupUsers();
                    } else {
                        operatorServer.getCentralDownUsers().putAll(newUsers);
                    }
                    operatorServer.setLastTimeSynchronized(synchronizedTime);
                    operatorServer.backupConfiguration();

                    status = "MULTIPLE_USERS_ADDED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);
                    socComm.close();
                    break;
                }
                //</editor-fold>

                case TRANSFER: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    OperatorUser newUser;
                    Date synchronizedTime;

                    if (!operatorServer.getServers().contains(new Server(socComm.getAddress()))) {
                        status = "ERROR_UNKNOWN_SERVER";
                        System.out.println(threadInfo + status);
                        socComm.writeString(status);
                        socComm.close();
                        break;
                    }

                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    newUser = (OperatorUser) socComm.readObject();
                    synchronizedTime = (Date) socComm.readObject();

                    status = "ADDING_USER";
                    System.out.println(threadInfo + status);
                    //add
                    operatorServer.getUsers().putIfAbsent(newUser.getId(), newUser);
                    operatorServer.backupUsers();

                    operatorServer.setLastTimeSynchronized(synchronizedTime);
                    operatorServer.backupConfiguration();

                    //logging
                    OperatorLog log = operatorServer.getTransferLog();
                    log.logTransfer(synchronizedTime, newUser.getId(), newUser.getNumber(), newUser.getOldOperator());

                    status = "USER_TRANSFERED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);
                    socComm.close();
                    break;
                }
                //</editor-fold>

                case MULTIPLE_TRANSFER: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    ConcurrentHashMap<Integer, OperatorUser> newUsers;
                    Date synchronizedTime;

                    if (!operatorServer.getServers().contains(new Server(socComm.getAddress()))) {
                        status = "ERROR_UNKNOWN_SERVER";
                        System.out.println(threadInfo + status);
                        socComm.writeString(status);
                        socComm.close();
                        break;
                    }

                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    newUsers = (ConcurrentHashMap<Integer, OperatorUser>) socComm.readObject();
                    synchronizedTime = (Date) socComm.readObject();

                    status = "ADDING_MULTIPLE_USERS";
                    System.out.println(threadInfo + status);

                    operatorServer.getUsers().putAll(newUsers);
                    operatorServer.backupUsers();

                    operatorServer.setLastTimeSynchronized(synchronizedTime);
                    operatorServer.backupConfiguration();

                    //logging
                    OperatorLog log = operatorServer.getTransferLog();
                    String oldOperatorname = newUsers.elements().nextElement().getOldOperator();
                    log.logMultipleTransfers(synchronizedTime, newUsers, oldOperatorname);

                    status = "MULTIPLE_USERS_TRANSFERED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);
                    socComm.close();
                    break;
                }
                //</editor-fold>

                case REMOVE_USER: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    int userID;
                    String newOperatorName;
                    Date synchronizedTime;

                    if (!operatorServer.getServers().contains(new Server(socComm.getAddress()))) {
                        status = "ERROR_UNKNOWN_SERVER";
                        System.out.println(threadInfo + status);
                        socComm.writeString(status);
                        socComm.close();
                        break;
                    }

                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    userID = socComm.readInt();
                    newOperatorName = socComm.readString();
                    synchronizedTime = (Date) socComm.readObject();


                    status = "REMOVING_USER";
                    System.out.println(threadInfo + status);

                    OperatorUser user = operatorServer.getUsers().get(userID);
                    user.setCurrentOperator(newOperatorName);
                    operatorServer.setLastTimeSynchronized(synchronizedTime);

                    status = "USER_REMOVED";
                    System.out.println(threadInfo + status);

                    socComm.close();
                    break;
                }
                //</editor-fold>

                case REMOVE_MULTIPLE_USERS: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    ArrayList<Integer> userIDs;
                    String newOperatorName;
                    Date synchronizedTime;

                    if (!operatorServer.getServers().contains(new Server(socComm.getAddress()))) {
                        status = "ERROR_UNKNOWN_SERVER";
                        System.out.println(threadInfo + status);
                        socComm.writeString(status);
                        socComm.close();
                        break;
                    }

                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    userIDs = (ArrayList<Integer>) socComm.readObject();
                    newOperatorName = socComm.readString();
                    synchronizedTime = (Date) socComm.readObject();

                    status = "REMOVING_MULTIPLE_USERS";
                    System.out.println(threadInfo + status);

                    for (Iterator<Integer> it = userIDs.iterator(); it.hasNext();) {
                        int key = it.next();
                        operatorServer.getUsers().get(key).setCurrentOperator(newOperatorName);
                    }
                    operatorServer.setLastTimeSynchronized(synchronizedTime);

                    status = "MULTIPLE_USERS_REMOVED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);
                    socComm.close();
                    break;
                }
                //</editor-fold>

                case DELETE_USER: //<editor-fold defaultstate="collapsed" desc="...">
                {
                    int userID;
                    Date synchronizedTime;

                    if (!operatorServer.getServers().contains(new Server(socComm.getAddress()))) {
                        status = "ERROR_UNKNOWN_SERVER";
                        System.out.println(threadInfo + status);
                        socComm.writeString(status);
                        socComm.close();
                        break;
                    }

                    status = "REQUEST_ACCEPTED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);

                    userID = socComm.readInt();
                    synchronizedTime = (Date) socComm.readObject();

                    status = "DELETING_USER";
                    System.out.println(threadInfo + status);

                    operatorServer.getUsers().remove(userID);
                    operatorServer.setLastTimeSynchronized(synchronizedTime);

                    status = "USER_DELETED";
                    System.out.println(threadInfo + status);
                    socComm.writeString(status);
                    socComm.close();
                    break;

                }
                //</editor-fold>
            }

        } catch (ClassNotFoundException ex) {
            error = true;
            Logger.getLogger(OperatorOperatorThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SocketException ex) {
            error = true;
            Logger.getLogger(OperatorOperatorThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            error = true;
            Logger.getLogger(OperatorOperatorThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (OutOfMemoryError err) {
            error = true;
            Logger.getLogger(OperatorOperatorThread.class.getName()).log(Level.SEVERE, null, err);
            try {
                socComm.writeString("ERROR_OOM");
            } catch (IOException ex) {
            }
        } finally {
            try {
                socComm.close();
            } catch (IOException ex) {
                Logger.getLogger(OperatorOperatorThread.class.getName()).log(Level.SEVERE, null, ex);
            }
            status = "FINISHED";
            System.out.println(threadInfo + status);
        }
    }
}
